package com.cch.cooperation.api.common.session;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.cch.cooperation.api.auth.dto.LoginUserDTO;
import com.cch.cooperation.common.web.session.BaseSessionPool;
import com.cch.cooperation.common.web.session.Session;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RTransaction;
import org.redisson.api.RedissonClient;
import org.redisson.api.TransactionOptions;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 基于 redis 的会话池
 *
 * @author cch
 */
@Slf4j
@Component
public class RedisSessionPool implements BaseSessionPool {

    private static final String SESSION_KEY = "API:SESSION:";
    private static final String SESSION_ID_KEY = "API:SESSION_ID:";
    private static final Integer SESSION_EXPIRE_HOUR = 24 * 15;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public Session<LoginUserDTO> get(String accessToken) {
        return redissonClient.<Session<LoginUserDTO>>getBucket(SESSION_KEY + accessToken).get();
    }

    @Override
    public Session getById(String id) {
        String accessToken = redissonClient.<String>getBucket(SESSION_ID_KEY + id).get();
        return StrUtil.isNotEmpty(accessToken) ? get(accessToken) : null;
    }

    @Override
    public void add(Session userSession) {
        String key = SESSION_KEY + userSession.getAccessToken();
        String idKey = SESSION_ID_KEY + userSession.getSessionId();
        String oldToken = redissonClient.<String>getBucket(idKey).get();
        RTransaction transaction = redissonClient.createTransaction(TransactionOptions.defaults());
        try {
            if (StrUtil.isNotEmpty(oldToken)) {
                transaction.getBucket(SESSION_KEY + oldToken).delete();
                transaction.getBucket(idKey).delete();
            }
            transaction.getBucket(idKey).set(userSession.getAccessToken(), SESSION_EXPIRE_HOUR, TimeUnit.HOURS);
            transaction.getBucket(key).set(userSession, SESSION_EXPIRE_HOUR, TimeUnit.HOURS);
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            throw e;
        }
    }

    public void update(Session userSession) {
        delete(userSession.getAccessToken());
        add(userSession);
    }

    @Override
    public void delete(String accessToken) {
        Session userSession = get(accessToken);
        if (ObjectUtil.isNotNull(userSession)) {
            RTransaction transaction = redissonClient.createTransaction(TransactionOptions.defaults());
            try {
                transaction.getBucket(SESSION_KEY + accessToken).delete();
                transaction.getBucket(SESSION_ID_KEY + userSession.getSessionId()).delete();
                transaction.commit();
            } catch (Exception e) {
                transaction.rollback();
                throw e;
            }
        }
    }
}
